/*
 * ===================================================================
 *  
 *      Filename: log_srv_mem_pool.c
 *
 *   Description: This is the memory pool for the hplogd.With the memory
 *                pool,we can reduce the risk of the memory leak,and the
 *                performace of the system could be improved slignt.
 *
 *       Version:  1.0
 *        Create:  2010/07/13
 *
 *        Author:  Keven Zhu     kevenzhu@tencent.com
 *     Copyright:  Reserved by Tencent.com Co.ltd
 *   
 * ====================================================================
 */
#include "lib/log_srv_mem_pool.h"

/*the number of the elements of each queue*/
size_t	max_list_elts;	

/*globle memory array point*/
mem_block_head *global_memory_array = NULL;

/*define the global memory array*/
mem_block_head *mem_pool_array = NULL;

/*initialize function for the memory_pool*/
int mem_pool_init(size_t max_elts)
{
	max_list_elts = max_elts;
    global_memory_array = (mem_block_head*)malloc(sizeof(mem_block_head)*ARRAY_SIZE + 16); //add 16 for the align
    if(!global_memory_array)
        return 0;
    mem_pool_array = (mem_block_head*)(((long)global_memory_array + 15)& (~15));
	memset((void*)mem_pool_array,0,sizeof(mem_block_head)*ARRAY_SIZE);	
	return 1;
}

/*internal function*/
mem_block*	get_block_head(char* buffer)
{
	return CONTAINER_OF(buffer,mem_block,buf);
}

/*find the sutiable size for the request */
uint32_t	get_index(size_t size,size_t* alignd_size)
{	
	*alignd_size = size;
	
	if (size > SYSTEM)
		return (uint32_t)-1;
	if (size == 0){
		*alignd_size = EXTRM_SMALL;
		return 0;
	}
	if (size > EXTRM_LARGE)
		return (((*alignd_size = ALIGN(size,EXTRM_LARGE)) >> 17) + 35);
	if (size > LARGE)
		return (((*alignd_size = ALIGN(size,LARGE)) >> 14) + 28);
	if (size > MID )
	       	return (((*alignd_size = ALIGN(size,MID)) >> 10) + 13);
	if (size > SMALL)
		return (((*alignd_size = ALIGN(size,SMALL)) >> 7) + 6);	
	return (((*alignd_size = ALIGN(size,EXTRM_SMALL)) >> 4) - 1);
}

/*internal function for the memory pool,list operation for the same size*/
__inline__ mem_block*	pop_front(volatile mem_block_head* lf)
{
	volatile mem_block* head;
	volatile mem_block* next;	
	unsigned long oc;
	do{
		head = 	lf->buf.next;
		oc = lf->pop_count;
		if (head == NULL) return NULL;
		next = head->buf.next;
	}while(!CAS2(&(lf->pop_count),(unsigned long)oc,(volatile unsigned long)head,(unsigned long)(oc+1),(unsigned long)next));
	atomic_dec(&(lf->size));
	return (mem_block*)head;	
}

/*try to get the first buffer from the list*/		
char*	get_first_fit(uint32_t idx, uint32_t size)
{
	
	if (idx == (uint32_t)-1)
    {
		return (char*)malloc(size);		
	}
	volatile mem_block_head* head = &mem_pool_array[idx];
	char* ret_ptr = NULL;
	mem_block* tmp = pop_front(head);
	if (tmp)
    {
		ret_ptr = tmp->buf.buf;
	}
	else 
    {	
		mem_block* k_block = (mem_block*)malloc(sizeof(uint32_t) + size);
		if (k_block == NULL)
        {
			perror("get_first_fit not have enought memory");
			return NULL;
		}
		k_block->size = size;
		ret_ptr = k_block->buf.buf;
	}
	return ret_ptr;
}

/*MAIN interface for the allocation, PLEASE use it!!!!!!*/
char*	hplogd_malloc(size_t size)
{
	size_t	alignd_size = 0;
	size_t	index = get_index(size,&alignd_size);			
	return get_first_fit(index,alignd_size);										
}

/*memory allocatation interface, and set the memory to zero*/
char*	hplogd_zalloc(size_t size)
{
	char* ret_ptr = hplogd_malloc(size);	
	memset(ret_ptr,0,size);
	return ret_ptr;
}

/*internal function for the memory pool,list operation for the same size*/
__inline__ int	push_head(volatile mem_block_head* lf, mem_block* cl)
{
	do 
    {
	    cl->buf.next = lf->buf.next;
	}while (!CAS((volatile void*)&lf->buf.next,(unsigned long)cl->buf.next,(unsigned long)cl));
	atomic_inc(&(lf->size));
	return 0;
}

/*internal function for the free functionality*/
void	revoke_free(uint32_t idx, char* buf)
{
	if (idx == (uint32_t)-1)
    {
		free(buf);
		return ;
	}		
	
	mem_block* block = get_block_head(buf);
	volatile mem_block_head* head = &mem_pool_array[idx];
	if (head->size >= max_list_elts)
    {
		free(block);
		return ;	
	}
	push_head(head,block);	
}

/*internal function*/
uint32_t get_bufsiz(char* buffer)
{
	if (buffer == NULL)
		return 0;	
	mem_block* tmp = get_block_head(buffer);
	return tmp->size;	
}

/*free the sigle buffer*/
void 	hplogd_free(char* buf)
{
	size_t 	size = get_bufsiz(buf);
	size_t	alignd_size = 0;
	uint32_t	index = get_index(size,&alignd_size);
	revoke_free(index,buf);				
}

/*aligned function manually*/
uint32_t get_alignd_size(size_t size)
{	
	if (size > SYSTEM)
		return size;
	if (size == 0)
		return EXTRM_SMALL;
	if (size > EXTRM_LARGE)
		return ALIGN(size,EXTRM_LARGE);
	if (size > LARGE)
		return ALIGN(size,LARGE);
	if (size > MID )
	       	return ALIGN(size,MID);
	if (size > SMALL)
		return ALIGN(size,SMALL);
	return ALIGN(size,EXTRM_SMALL);
}

/*destroy the memory pool and free all the cached buffers*/
void mem_pool_destroy()
{
    int k = 0;
	for ( k = 0 ; k < ARRAY_SIZE ; k++)
    {
		volatile mem_block_head* head = &mem_pool_array[k];
		mem_block* tmp;
		while ((tmp = pop_front(head)))
        {
			free(tmp);		
		}				
	}
    free(global_memory_array);    
}

/*debug function for the statistics*/
void show_cur_stat()
{
	int total_elts = 0;
    int k = 0;
	for (k = 0 ; k < ARRAY_SIZE; k++)
    {
		volatile mem_block_head* head = &mem_pool_array[k];
		uint32_t siz = head->size;
		//cerr << "\nmem_pool_array[ "<< k << "].size=" << siz << ": ";
		total_elts += siz;
	/*	mem_block* tmp = head;
		int count = 0;
		while ((tmp = tmp->buf.next)){
			cerr << "<" << ++count << ". " << tmp->size << "," << (unsigned long)tmp->buf.buf << ">;";
			total_elts++;
		}
		cerr << "\nelements at mem_pool_array[" << k << "] :" << count << "\n";*/
	}
	//cerr << "each list have max elts:" << max_list_elts <<", pool elements total:" << total_elts << endl;
}

